package service

import (
	"fmt"
	"helper-api/app/models/request"
	"helper-api/app/models/response"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
)

var File = new(FileService)

type FileService struct{}

// FileList 文件列表
func (f *FileService) FileList(param *request.FileListQueryRequest) (*response.PageData, *response.BusinessError) {
	var (
		err     error
		items   []os.DirEntry
		info    *response.FileListResponse
		convert = func(parentPath string, target os.DirEntry) (*response.FileListResponse, error) {
			var (
				result      *response.FileListResponse
				getUserName = func(uid uint32) string {
					usr, err := user.LookupId(strconv.Itoa(int(uid)))
					if err != nil {
						return ""
					}
					return usr.Username
				}
				getGroup = func(gid uint32) string {
					usr, err := user.LookupGroupId(strconv.Itoa(int(gid)))
					if err != nil {
						return ""
					}
					return usr.Name
				}
				getSymlink = func(path string) string {
					linkPath, err := os.Readlink(path)
					if err != nil {
						return ""
					}
					return linkPath
				}
				getMineType = func(path string) string {
					file, err := os.Open(path)
					if err != nil {
						return ""
					}
					defer file.Close()

					buffer := make([]byte, 512)
					_, err = file.Read(buffer)
					if err != nil {
						return ""
					}
					mimeType := http.DetectContentType(buffer)
					return mimeType
				}
				fileInfo os.FileInfo
				path     string
				isLink   bool
				err      error
			)
			if fileInfo, err = target.Info(); err != nil {
				return nil, err
			}
			path = filepath.Join(parentPath, target.Name())
			result = &response.FileListResponse{
				Name:       target.Name(),
				Path:       path,
				Perm:       fmt.Sprintf("%04o", fileInfo.Mode().Perm()),
				User:       getUserName(fileInfo.Sys().(*syscall.Stat_t).Uid),
				Group:      getGroup(fileInfo.Sys().(*syscall.Stat_t).Gid),
				Size:       fileInfo.Size(),
				UpdateTime: fileInfo.ModTime(),
				IsDir:      target.IsDir(),
				IsHidden:   strings.HasPrefix(target.Name(), "."),
				MimeType:   getMineType(path),
			}
			isLink = fileInfo.Mode()&os.ModeSymlink != 0
			result.IsLink = isLink
			if isLink {
				result.LinkPath = getSymlink(path)
			}
			return result, nil
		}
		total   int
		start   int
		end     int
		records []*response.FileListResponse
	)
	// 判断是否为目录
	if _, err = os.Stat(param.Path); err != nil && os.IsNotExist(err) {
		return nil, response.NewBusinessError(response.FileNotDir)
	}
	// 读取目录
	if items, err = os.ReadDir(param.Path); err != nil {
		return nil, response.NewBusinessError(response.ReadFileListError)
	}
	for _, item := range items {

		if info, err = convert(param.Path, item); err != nil {
			continue
		}
		if param.ShowHidden {
			// 展示隐藏文件
			if param.Search != "" {
				if strings.Contains(info.Name, param.Search) {
					records = append(records, info)
				}
			} else {
				records = append(records, info)
			}
		} else {
			// 不展示隐藏文件
			if !info.IsHidden {
				if param.Search != "" {
					if strings.Contains(info.Name, param.Search) {
						records = append(records, info)
					}
				} else {
					records = append(records, info)
				}
			}
		}
	}
	total, start, end = len(records), (param.Page-1)*param.Size, param.Page*param.Size
	if start > total {
		// 当前页没有数据，直接可以返回
		return response.NewPageData(param.Page, param.Size, int64(total), records), nil
	}
	if end >= total {
		end = total
	}
	// 重新映射新的返回对象
	return response.NewPageData(param.Page, param.Size, int64(total), records[start:end]), nil
}

// FileCreate 文件创建
func (f *FileService) FileCreate(r *request.ToolsFileCreateRequest) *response.BusinessError {
	return nil
}
